﻿    using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Data.DataControl;

namespace System.Data.DataModel
{
    /// <summary>
    /// This Class Create Model Class and CRUD for Database by Properties.
    /// Declare class name, properties,  Database Provider and connection string for database
    /// Make class file {namespace(you can decide it)}.{your new class} at anywhere you want.
    /// </summary>
    public class ModelCreator
    {

        private string assemblyName;

        private string className;

        private List<PropertySet> properties = new List<PropertySet>();

        private const string IMPORT_TEXT = "using System;\r\n" +
                                       "using System.Collections.Generic;\r\n" +
                                       "using System.Linq;\r\n"  +
                                       "using System.Text;\r\n"  +
                                       "using System.Data.DataControl;\r\n" +
                                       "using System.Data.DataModel;";

        private const string NAME_SPACE = "namespace ";

        private const string EXTEND_CLASS = ":BaseModel";


        private string classFileString;

        private bool dropTable = false;

        


        public ModelCreator()
        {
        }

        /// <summary>
        /// Model Creator Constructor
        /// </summary>
        /// <param name="assemblyName">Simple Assembly Name like current project name</param>
        /// <param name="className">Class name</param>
        /// <param name="properties">Property List Like {"Name=ContactID;Type=System.Double;Size=0;Primary=true"}
        /// Declare PropertSet {Name(property name)}, {Type(datatype like System.***)}, {Size(for database column size)}, {Primary(for database primarykey)}</param>
        /// <param name="dropTable">Recreate Table or not</param>
        /// <param name="dataProvider">What database you use?</param>
        /// <param name="connectionString">Connection String</param>
        public ModelCreator(string assemblyName, string className, string[] properties, bool dropTable)
        {
            this.assemblyName = assemblyName;
            this.className = className;
            Array.FindAll<string>(properties, delegate(string content)
            {

                this.properties.Add(new PropertySet(content));

                return true;
            });
            this.dropTable = dropTable;
      
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyName">Simple Assembly Name like current project name</param>
        /// <param name="classContents">Array for set more parameters: Class, Property, DropTable</param>
        /// <param name="dataProvider">What database you use?</param>
        /// <param name="connectionString">Connection String</param>
        public ModelCreator(string assemblyName, string[] classContents)
        {          

            this.assemblyName = assemblyName;
            Array.FindAll<string>(classContents, delegate(string content){
                if (content.Contains("Class:"))
                {
                    this.className = content.Split(':')[1];
                }
                else if (content.Contains("Property:"))
                {
                    this.properties.Add(new PropertySet(content.Split(':')[1]));

                }
                else if (content.Contains("DropTable:"))
                {
                    this.dropTable = bool.Parse(content.Split(';')[1]);
                }
                return true;
            });


        }

        /// <summary>
        /// Build Class Text
        /// </summary>
        public void SetClassText()
        {
            //Class text builder
            StringBuilder builder = new StringBuilder();
            //using
            builder.AppendLine(IMPORT_TEXT);
            builder.AppendLine();
            //name space
            builder.AppendLine(NAME_SPACE + this.assemblyName + ".Model");
            builder.AppendLine("{");

            //Add Class
            builder.AppendLine("\tpublic class " + this.className + EXTEND_CLASS);
            builder.AppendLine("\t{");

            //Constructor
            builder.AppendLine("\t\tpublic " + this.className + "() : base()");
            builder.AppendLine("\t\t{");
             // Query Commands
            builder.AppendLine("\t\t\tthis.dbController.SelectAllCommand = this.selectAllString;");
            builder.AppendLine("\t\t\tthis.dbController.SelectCommand = this.selectString;");
            builder.AppendLine("\t\t\tthis.dbController.InsertCommand = this.insertString;");
            builder.AppendLine("\t\t\tthis.dbController.UpdateCommand = this.updateString;");
            builder.AppendLine("\t\t\tthis.dbController.DeleteCommand = this.deleteString;");
            builder.AppendLine("\t\t}");

            //Select Query Builder
            StringBuilder selectBuilder = new StringBuilder();
            selectBuilder.Append("\"SELECT ");
            //Update Query Builder
            StringBuilder updateBuilder = new StringBuilder();
            updateBuilder.Append("\"UPDATE " + this.className + " SET ");
            //Insert Query Builder
            StringBuilder insertBuilder = new StringBuilder();
            insertBuilder.Append("\"INSERT INTO " + this.className + "(");
            //Insert Query Values Builder
            StringBuilder insertValuesBuilder = new StringBuilder();
            insertValuesBuilder.Append(" VALUES(");
            //Delete Query
            string deleteString = "\"DELETE FROM " + this.className;
            //Class Property Builder
            StringBuilder propertyBuilder = new StringBuilder();
            //Condition
            string conditionString = String.Empty;
            int pkCount = 0;
            this.properties.ForEach(delegate(PropertySet property)
            {
                //Property Add
                propertyBuilder.AppendLine("\t\t" + "public " + property.Type + " " + property.Name + "{ get; set; }\r\n");

                //Select Column Add
                selectBuilder.Append("" + property.Name + ",");  
                insertBuilder.Append(" " + property.Name + ",");
                insertValuesBuilder.Append(" " + this.GetParameterMark() + property.Name + ",");

                if (property.PrimaryKey)
                {
                    // Set Condition for PK
                    conditionString += (pkCount == 0 ? " WHERE " : " AND ") + property.Name + " = " +
                        this.GetParameterMark() + property.Name;
                    pkCount++;
                }
                else
                {
                    // Add Edit Columns

                    updateBuilder.Append(" " + property.Name + "=" + this.GetParameterMark() + property.Name + ",");
                }
                
            });
            //Remove Last Comma
            selectBuilder.Remove(selectBuilder.Length - 1, 1);
            updateBuilder.Remove(updateBuilder.Length - 1, 1);
            insertBuilder.Remove(insertBuilder.Length - 1, 1);
            insertValuesBuilder.Remove(insertValuesBuilder.Length - 1, 1);

            //Close ) of Insert Query
            insertBuilder.Append(")");
            insertValuesBuilder.Append(")");

            //Edit Select Query
            selectBuilder.Append(" FROM " + this.className + " ");
            //Add All Select 
            builder.AppendLine("\t\tprivate string selectAllString = " + selectBuilder.ToString() + "\";");
            //Add Condition
            selectBuilder.Append(conditionString);
            updateBuilder.Append(conditionString);
            deleteString += conditionString;

            //Add Query String Fields
            builder.AppendLine("\t\tprivate string selectString = " + selectBuilder.ToString() + "\";");
            builder.AppendLine("\t\tprivate string updateString = " + updateBuilder.ToString() + "\";");
            builder.AppendLine("\t\tprivate string insertString = " + insertBuilder.ToString() + insertValuesBuilder.ToString() + "\";");
            builder.AppendLine("\t\tprivate string deleteString = " + deleteString + "\";");

            //Add Properties
            builder.AppendLine(propertyBuilder.ToString());

            builder.AppendLine("\t}");
            builder.AppendLine("}");

            this.classFileString = builder.ToString();
            Console.WriteLine(this.classFileString);
        }


        /// <summary>
        /// Parameter Mark for various Databases
        /// </summary>
        /// <returns></returns>
        private string GetParameterMark()
        {
            string dbProvider =
            System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ProviderName;
            switch (dbProvider)
            {
                case "System.Data.SqlClient":
                    return "@" ;
                case "Oracle.DataAccess.Client":
                    return ":";
                default:
                    return "@";
            }
        }


        /// <summary>
        /// Create Clas File
        /// </summary>
        /// <param name="path"></param>
        public void CreateFile(string path)
        {
            //ToDo Try Catch
            string fullPath = path +  "Model\\";
            string filePath = fullPath + this.className + ".cs";
            Directory.CreateDirectory(fullPath);

            File.WriteAllText(filePath, this.classFileString);


        }

        /// <summary>
        /// Create Table
        /// </summary>
        public void SetDatabaseObject(string filePath)
        {
            IDbController dbController = DbController.DbControllerFactory();
            dbController.CreateTable(this.dropTable, this.className, this.properties, filePath);

        }
        


    }
}
